package cn.virens.web.controller.manage.wxmp;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;

import cn.hutool.core.util.IdUtil;
import cn.virens.Assert;
import cn.virens.common.RequestUtil;
import cn.virens.components.page.PageParamUser;
import cn.virens.components.page.PageResult;
import cn.virens.database.common.TransactionExecute;
import cn.virens.database.mysql.model.wxmp.WxmpMenus;
import cn.virens.database.mysql.model.wxmp.WxmpMenusButton;
import cn.virens.database.mysql.model.wxmp.WxmpMenusMatchrule;
import cn.virens.exception.APIException;
import cn.virens.service.inter.platform.wxmp.WxmpBaseService;
import cn.virens.service.inter.platform.wxmp.WxmpMenusService;
import cn.virens.service.inter.platform.wxmp.menus.WxmpMenusButtonService;
import cn.virens.service.inter.platform.wxmp.menus.WxmpMenusMatchruleService;
import cn.virens.web.common.BaseTableController;
import cn.virens.web.components.open.wxmp.WxMpServiceImpl;
import me.chanjar.weixin.common.bean.menu.WxMenu;
import me.chanjar.weixin.common.bean.menu.WxMenuButton;
import me.chanjar.weixin.common.bean.menu.WxMenuRule;
import me.chanjar.weixin.mp.bean.menu.WxMpGetSelfMenuInfoResult;
import me.chanjar.weixin.mp.bean.menu.WxMpMenu;
import me.chanjar.weixin.mp.bean.menu.WxMpMenu.WxMpConditionalMenu;
import me.chanjar.weixin.mp.bean.menu.WxMpSelfMenuInfo;
import me.chanjar.weixin.mp.bean.menu.WxMpSelfMenuInfo.WxMpSelfMenuButton;
import me.chanjar.weixin.mp.bean.menu.WxMpSelfMenuInfo.WxMpSelfMenuButton.SubButtons;

/**
 * 菜单管理 - wxmp_menus
 *
 * @author Virens
 * @since  2019-11-09
 */
@Controller
@RequestMapping("/manage/wxmp/menus")
public class WxmpMenusContorller extends BaseTableController<WxmpMenus, WxmpMenusService> {
	private @Autowired WxmpMenusMatchruleService mWxmpMenusMatchruleService;
	private @Autowired WxmpMenusButtonService mWxmpMenusButtonService;
	private @Autowired TransactionExecute mTransactionExecute;
	private @Autowired WxmpBaseService mWxmpBaseService;
	private @Autowired WxMpServiceImpl mWxService;

	public WxmpMenusContorller() {
		super("/manage/wxmp/menus", "wxmp:menus");
	}

	@Override
	public String index(HttpServletRequest request, HttpServletResponse response, Model model) {
		model.addAttribute("appid", RequestUtil.getValueStr(request, "appid"));

		return super.index(request, response, model);
	}

	@Override
	protected PageResult<?> listHandle(HttpServletRequest request, PageParamUser pageParam) throws Exception {
		pageParam.addParam("_appid", RequestUtil.getValueStr(request, "_appid"));
		pageParam.addParam("appid", RequestUtil.getValueStr(request, "appid"));
		pageParam.addParam("title", RequestUtil.getValueStr(request, "title"));

		return super.listHandle(request, pageParam);
	}

	@RequestMapping("view.jspx")
	public String viewJspx(HttpServletRequest request, HttpServletResponse response, Model model) {
		model.addAttribute("record", mBaseService.selectOne(RequestUtil.getValueInt(request, "id")));

		return returnUrl("view");
	}

	@RequestMapping("create.jspx")
	public String createJspx(HttpServletRequest request, HttpServletResponse response, Model model) {
		model.addAttribute("wxmps", mWxmpBaseService.selectAll());

		return returnUrl("create");
	}

	@RequestMapping("update.jspx")
	public String updateJspx(@RequestParam("uuid") String uuid, HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
		model.addAttribute("matchrule", mWxmpMenusMatchruleService.selectByUuid(uuid));
		model.addAttribute("button", mWxmpMenusButtonService.selectByUuid(uuid));
		model.addAttribute("record", mBaseService.selectByUuid(uuid));
		model.addAttribute("wxmps", mWxmpBaseService.selectAll());

		return returnUrl("update");
	}

	@ResponseBody
	@RequestMapping(value = "save.json", params = "oper=create0", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
	public Model createJSON(@RequestParam("appid") String appid, @RequestBody JSONObject record, Model model) throws Exception {
		return mTransactionExecute.write((s) -> mWxService.switchover(appid, () -> {
			WxmpMenus menus = new WxmpMenus();
			menus.setAppid(mWxService.getAppid());
			menus.setUuid(IdUtil.fastSimpleUUID());
			menus.setTitle(record.getString("title"));

			if ((menus = mBaseService.insert(menus)) != null) {
				this.saveConditionalMenu(menus, record);
			} else {
				throw new APIException("ERROR", "保存菜单失败");
			}

			return renderSuccess(model, "创建成功");
		}));
	}

	@ResponseBody
	@RequestMapping(value = "save.json", params = "oper=update0", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
	public Model updateJSON(@RequestParam("appid") String appid, @RequestBody JSONObject record, Model model) throws Exception {
		return mTransactionExecute.write((s) -> mWxService.switchover(appid, () -> {
			mWxmpMenusMatchruleService.deleteByUuid(record.getString("uuid"));
			mWxmpMenusButtonService.deleteByUuid(record.getString("uuid"));
			mBaseService.deleteByUuid(record.getString("uuid"));

			// 删除完已有的菜单后，重新进行添加
			WxmpMenus menus = new WxmpMenus();
			menus.setAppid(mWxService.getAppid());
			menus.setUuid(record.getString("uuid"));
			menus.setTitle(record.getString("title"));

			if ((menus = mBaseService.insert(menus)) != null) {
				this.saveConditionalMenu(menus, record);
			} else {
				throw new APIException("ERROR", "保存菜单失败");
			}

			return renderSuccess(model, "创建成功");
		}));
	}

	@ResponseBody
	@RequestMapping("delete.json")
	public Model delete(@RequestParam(name = "uuid", required = false) String uuid, Model model) throws Exception {
		return mTransactionExecute.write((s) -> {
			WxmpMenus record = mBaseService.selectByUuid(uuid);
			if (record == null) throw new APIException("NULL", "菜单不存在");

			// 先从数据库删除菜单
			mWxmpMenusMatchruleService.deleteByUuid(record.getUuid());
			mWxmpMenusButtonService.deleteByUuid(record.getUuid());
			mBaseService.deleteByUuid(record.getUuid());

			// 从微信服务器删除菜单
			return mWxService.switchover(record.getAppid(), () -> {
				if (record.getMenuid() != null && !record.getMenuid().isEmpty()) {
					this.mWxService.getMenuService().menuDelete(record.getMenuid());
				} else {
					this.mWxService.getMenuService().menuDelete();
				}

				return renderSuccess(model, "删除成功");
			});
		});

	}

	@ResponseBody
	@RequestMapping("push.json")
	public Model push(@RequestParam("appid") String appid, @RequestParam("uuid") String uuid, Model model) throws Exception {
		return mTransactionExecute.write((s) -> mWxService.switchover(appid, () -> {
			WxmpMenus menus = mBaseService.selectByUuid(uuid);
			if (menus == null) return renderFail(model, "菜单不存在");

			// 如果该菜单已经推送了，就先删除对应的菜单
			if (menus.getMenuid() != null && !menus.getMenuid().isEmpty()) {
				this.mWxService.getMenuService().menuDelete(menus.getMenuid());
			}

			WxMenu mpMenu = new WxMenu();

			// 查询菜单匹配规则，如果存在匹配规则就要将规则添加进菜单
			WxmpMenusMatchrule wxmpMenusMatchrule = mWxmpMenusMatchruleService.selectByUuid(uuid);
			if (wxmpMenusMatchrule != null && wxmpMenusMatchrule.getUuid() != null) {
				WxMenuRule matchRule = new WxMenuRule();

				matchRule.setClientPlatformType(wxmpMenusMatchrule.getClientPlatformType());
				matchRule.setLanguage(wxmpMenusMatchrule.getLanguage());
				matchRule.setProvince(wxmpMenusMatchrule.getProvince());
				matchRule.setCountry(wxmpMenusMatchrule.getCountry());
				matchRule.setTagId(wxmpMenusMatchrule.getTagId());
				matchRule.setCity(wxmpMenusMatchrule.getCity());
				matchRule.setSex(wxmpMenusMatchrule.getSex());

				mpMenu.setMatchRule(matchRule);
			}

			// 查询该分类下的所有菜单，并按照层级关系进行构造
			List<WxmpMenusButton> list = mWxmpMenusButtonService.selectByUuid(uuid);
			mpMenu.setButtons(list.stream().filter((a) -> {
				return a.getPid() == null || a.getPid() == 0L;
			}).map((a) -> {
				WxMenuButton menuButton = pasre(a);
				menuButton.setSubButtons(list.stream().filter((b) -> {
					return b.getPid() != null && b.getPid() == a.getId();
				}).map((b) -> pasre(b)).collect(Collectors.toList()));

				return menuButton;
			}).collect(Collectors.toList()));

			// 将菜单推送到微信，如果有返回菜单编号，就将菜单编号刷新进数据库
			String menuidResult = mWxService.getMenuService().menuCreate(mpMenu);
			if (menuidResult != null && !menuidResult.isEmpty()) {
				menus.setMenuid(menuidResult);

				mBaseService.update(menus);
			}

			return renderSuccess(model, "推送成功");
		}));
	}

	@ResponseBody
	@RequestMapping("sycn.json")
	public Model sycn(@RequestParam("appid") String appid, HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
		return mTransactionExecute.write((s) -> mWxService.switchover(appid, () -> {
			WxMpMenu result = mWxService.getMenuService().menuGet();

			// 获取默认菜单，并存入数据库
			if (result != null && result.getMenu() != null) {
				WxmpMenus menus = new WxmpMenus();
				menus.setTitle("默认菜单");
				menus.setAppid(mWxService.getAppid());
				menus.setUuid(IdUtil.fastSimpleUUID());
				menus.setMenuid(result.getMenu().getMenuId());

				if ((menus = mBaseService.insert(menus)) != null) {
					this.saveConditionalMenu(menus, result.getMenu());
				} else {
					throw new APIException("ERROR", "保存菜单失败");
				}
			}

			// 获取全部非默认菜单，并添加到数据库
			for (WxMpConditionalMenu conditionalMenu : safeList0(result.getConditionalMenu())) {
				WxmpMenus menuOther = new WxmpMenus();
				menuOther.setAppid(mWxService.getAppid());
				menuOther.setUuid(IdUtil.fastSimpleUUID());
				menuOther.setTitle(conditionalMenu.getMenuId());
				menuOther.setMenuid(conditionalMenu.getMenuId());

				if (result != null && (menuOther = mBaseService.insert(menuOther)) != null) {
					this.saveConditionalMenu(menuOther, conditionalMenu);
				} else {
					throw new APIException("ERROR", "保存菜单失败");
				}
			}

			return renderSuccess(model, "同步成功");
		}));
	}

	@ResponseBody
	@RequestMapping("sycn/base.json")
	public Model sycnBase(@RequestParam("appid") String appid, HttpServletRequest request, HttpServletResponse response, Model model) throws Exception {
		return mTransactionExecute.write((s) -> mWxService.switchover(appid, () -> {
			WxMpGetSelfMenuInfoResult result = mWxService.getMenuService().getSelfMenuInfo();

			WxmpMenus menus = new WxmpMenus();
			menus.setAppid(mWxService.getAppid());
			menus.setUuid(IdUtil.fastSimpleUUID());
			menus.setTitle("默认菜单");

			if (result != null && (menus = mBaseService.insert(menus)) != null) {
				for (WxMpSelfMenuButton firstButton : safeList(result.getSelfMenuInfo())) {
					WxmpMenusButton buttonOne = pasre(firstButton);
					buttonOne.setUuid(menus.getUuid());

					if ((buttonOne = mWxmpMenusButtonService.insert(buttonOne)) != null) {
						for (WxMpSelfMenuButton secondButton : safeList(firstButton.getSubButtons())) {
							WxmpMenusButton buttonTwo = pasre(secondButton);
							buttonTwo.setPid(buttonOne.getId());
							buttonTwo.setUuid(menus.getUuid());

							if (mWxmpMenusButtonService.insert(buttonTwo) == null) {//
								throw new APIException("ERROR", "保存菜单失败");
							}
						}
					} else {
						throw new APIException("ERROR", "保存菜单失败");
					}
				}
			} else {
				throw new APIException("ERROR", "保存菜单失败");
			}

			return renderSuccess(model, "同步成功");
		}));
	}

	private WxmpMenusButton pasre(WxMenuButton menuButton) {
		WxmpMenusButton answer = new WxmpMenusButton();
		answer.setPagepath(menuButton.getPagePath());
		answer.setAppid(menuButton.getAppId());
		answer.setName(menuButton.getName());
		answer.setType(menuButton.getType());
		answer.setKey(menuButton.getKey());
		answer.setUrl(menuButton.getUrl());

		return answer;
	}

	private WxMenuButton pasre(WxmpMenusButton button) {
		WxMenuButton answer = new WxMenuButton();
		answer.setPagePath(button.getPagepath());
		answer.setMediaId(button.getMediaId());
		answer.setAppId(button.getAppid());
		answer.setName(button.getName());
		answer.setType(button.getType());
		answer.setKey(button.getKey());
		answer.setUrl(button.getUrl());

		return answer;
	}

	private WxmpMenusButton pasre(WxMpSelfMenuButton menuButton) {
		WxmpMenusButton answer = new WxmpMenusButton();
		answer.setPagepath(menuButton.getPagePath());
		answer.setAppid(menuButton.getAppId());
		answer.setName(menuButton.getName());
		answer.setType(menuButton.getType());
		answer.setKey(menuButton.getKey());
		answer.setUrl(menuButton.getUrl());

		return answer;
	}

	private static boolean isNotEmpty(JSONObject obj) {
		for (Object o : obj.values()) {
			if (o != null && !"".equals(o)) {//
				return true;
			}
		}
		return false;
	}

	private List<WxMpSelfMenuButton> safeList(SubButtons subButtons) {
		if (subButtons == null || subButtons.getSubButtons() == null) {//
			return new ArrayList<>();
		}

		return subButtons.getSubButtons();
	}

	private List<WxMpSelfMenuButton> safeList(WxMpSelfMenuInfo menuInfo) {
		if (menuInfo == null || menuInfo.getButtons() == null) {//
			return new ArrayList<>();
		}

		return menuInfo.getButtons();
	}

	private List<WxMenuButton> safeList(WxMpConditionalMenu subButtons) {
		if (subButtons == null || subButtons.getButtons() == null) {//
			return new ArrayList<>();
		}

		return subButtons.getButtons();
	}

	private List<WxMenuButton> safeList1(List<WxMenuButton> subButtons) {
		return subButtons != null ? subButtons : new ArrayList<>();
	}

	private List<WxMpConditionalMenu> safeList0(List<WxMpConditionalMenu> conditionalMenu) {
		return conditionalMenu != null ? conditionalMenu : new ArrayList<>();
	}

	private void saveConditionalMenu(WxmpMenus menus, JSONObject record) {
		// 保存菜单按钮列表------------------------------------------------------------
		JSONArray array = record.getJSONArray("buttons");
		Assert.isEmpty(array, "BTN_EMPTY", "按钮为空");

		for (int i = 0; i < array.size(); i++) {
			JSONObject obj = array.getJSONObject(i);
			if (obj == null || obj.isEmpty()) continue;

			WxmpMenusButton buttonOne = new WxmpMenusButton();
			buttonOne.setPagepath(obj.getString("pagepath"));
			buttonOne.setAppid(obj.getString("appid"));
			buttonOne.setName(obj.getString("name"));
			buttonOne.setType(obj.getString("type"));
			buttonOne.setKey(obj.getString("key"));
			buttonOne.setUrl(obj.getString("url"));
			buttonOne.setUuid(menus.getUuid());

			if ((buttonOne = mWxmpMenusButtonService.insert(buttonOne)) != null) {
				JSONArray subList = obj.getJSONArray("sub");
				if (subList == null || subList.isEmpty()) continue;

				for (int i2 = 0; i2 < subList.size(); i2++) {
					JSONObject obj2 = subList.getJSONObject(i2);
					if (obj2 == null || obj2.isEmpty()) continue;

					WxmpMenusButton buttonTwo = new WxmpMenusButton();
					buttonTwo.setPagepath(obj2.getString("pagepath"));
					buttonTwo.setAppid(obj2.getString("appid"));
					buttonTwo.setName(obj2.getString("name"));
					buttonTwo.setType(obj2.getString("type"));
					buttonTwo.setKey(obj2.getString("key"));
					buttonTwo.setUrl(obj2.getString("url"));
					buttonTwo.setPid(buttonOne.getId());
					buttonTwo.setUuid(menus.getUuid());

					if (mWxmpMenusButtonService.insert(buttonTwo) == null) {//
						throw new APIException("ERROR", "保存菜单失败");
					}
				}
			} else {
				throw new APIException("ERROR", "保存菜单失败");
			}
		}

		// 保存菜单匹配规则------------------------------------------------------------
		JSONObject matchrule = record.getJSONObject("matchrule");
		if (matchrule != null && !matchrule.isEmpty() && isNotEmpty(matchrule)) {
			WxmpMenusMatchrule matchrule1 = new WxmpMenusMatchrule();
			matchrule1.setClientPlatformType(matchrule.getString("clientPlatformType"));
			matchrule1.setProvince(matchrule.getString("province"));
			matchrule1.setLanguage(matchrule.getString("language"));
			matchrule1.setCountry(matchrule.getString("country"));
			matchrule1.setTagId(matchrule.getString("tagId"));
			matchrule1.setCity(matchrule.getString("city"));
			matchrule1.setSex(matchrule.getString("sex"));
			matchrule1.setUuid(menus.getUuid());

			if (mWxmpMenusMatchruleService.insert(matchrule1) == null) {//
				throw new APIException("ERROR", "保存菜单规则失败");
			}
		}
	}

	private void saveConditionalMenu(WxmpMenus menus, WxMpConditionalMenu conditionalMenu) {
		for (WxMenuButton firstButton : safeList(conditionalMenu)) {
			WxmpMenusButton buttonOne = pasre(firstButton);
			buttonOne.setUuid(menus.getUuid());

			if ((buttonOne = mWxmpMenusButtonService.insert(buttonOne)) != null) {
				for (WxMenuButton secondButton : safeList1(firstButton.getSubButtons())) {
					WxmpMenusButton buttonTwo = pasre(secondButton);
					buttonTwo.setPid(buttonOne.getId());
					buttonTwo.setUuid(menus.getUuid());

					if (mWxmpMenusButtonService.insert(buttonTwo) == null) {//
						throw new APIException("ERROR", "保存菜单失败");
					}
				}
			} else {
				throw new APIException("ERROR", "保存菜单失败");
			}
		}

		// 处理菜单匹配规则
		if (menus.getUuid() != null && conditionalMenu.getRule() != null) {
			WxMenuRule rule = conditionalMenu.getRule();

			WxmpMenusMatchrule matchrule = new WxmpMenusMatchrule();
			matchrule.setClientPlatformType(rule.getClientPlatformType());
			matchrule.setProvince(rule.getProvince());
			matchrule.setLanguage(rule.getLanguage());
			matchrule.setCountry(rule.getCountry());
			matchrule.setTagId(rule.getTagId());
			matchrule.setUuid(menus.getUuid());
			matchrule.setCity(rule.getCity());
			matchrule.setSex(rule.getSex());

			if (mWxmpMenusMatchruleService.insert(matchrule) == null) {//
				throw new APIException("ERROR", "保存菜单规则失败");
			}
		}
	}

}
